#pragma once

#include <string>
#include <iterator>
#include <utility>
#include <algorithm>

#include "config/config_begin.h"
namespace cpp {

	template<class CharT, class Traits = std::char_traits<CharT> >
	class basic_string_view
	{
	public:
		using   traits_type = Traits;
		using	value_type = CharT;
		using	pointer = CharT*;
		using	const_pointer = const CharT*;
		using	reference = CharT&;
		using	const_reference = const CharT&;

		using	const_iterator = const_pointer;
		using	iterator = const_iterator;

		using	const_reverse_iterator = std::reverse_iterator < const_iterator >;
		using	reverse_iterator = const_reverse_iterator;

		using	size_type = std::size_t;
		using	difference_type = std::ptrdiff_t;

		static const size_type npos = size_type(-1);

		constexpr basic_string_view() noexcept :_data(nullptr), _size(0) {	}

		constexpr basic_string_view(const basic_string_view& other) noexcept = default;

		template<class Allocator>
		constexpr basic_string_view(const std::basic_string<CharT, Traits, Allocator>& str) noexcept
			:_data(str.data()), _size(str.size())
		{

		}

		constexpr basic_string_view(const CharT* s, size_type count) noexcept
			: _data(s), _size(count)
		{}

		constexpr basic_string_view(const CharT* s)
			: _data(s), _size(traits_type::length(s))
		{

		}

		basic_string_view& operator=(const basic_string_view& view) noexcept = default;

		constexpr const_iterator begin() const noexcept
		{
			return _data;
		}
		constexpr const_iterator cbegin() const noexcept
		{
			return begin();
		}

		constexpr const_iterator end() const noexcept
		{
			return _data + _size;
		}
		constexpr const_iterator cend() const noexcept
		{
			return end();
		}

		constexpr const_reverse_iterator rbegin() const noexcept
		{
			return const_reverse_iterator(end());
		}
		constexpr const_reverse_iterator crbegin() const noexcept
		{
			return rbegin();
		}

		constexpr const_reverse_iterator rend() const noexcept
		{
			return const_reverse_iterator(begin());
		}
		constexpr const_reverse_iterator crend() const noexcept
		{
			return rend();
		}
		constexpr const_reference operator[](size_type pos) const
		{
			return _data[pos];
		}
		constexpr const_reference at(size_type pos) const
		{
			if(pos >= _size)
			{
				throw std::out_of_range("cpp::basic_string_view:at");
			}
			return _data[pos];
		}

		constexpr const_reference front() const
		{
			return _data[0];
		}
		constexpr const_reference back() const
		{
			return _data[_size - 1];
		}
		constexpr const_pointer data() const noexcept
		{
			return _data;
		}
		constexpr size_type size() const noexcept
		{
			return _size;
		}
		constexpr size_type length() const noexcept
		{
			return _size;
		}

		constexpr size_type max_size() const noexcept
		{
			return  size_type(-1) / sizeof(value_type);
		}

		constexpr bool empty() const noexcept
		{
			return _size == 0;
		}

		//void clear() { _data=nullptr; _size = 0; }

		constexpr void remove_prefix(size_type n)
		{
			_data += n;
			_size -= n;
		}
		constexpr void remove_suffix(size_type n)
		{
			_size -= n;
		}

		constexpr void swap(basic_string_view& v) noexcept
		{
			std::swap(_data, v._data);
			std::swap(_size, v._size);
		}


		template<class Allocator = std::allocator<CharT>>
		std::basic_string<CharT, Traits, Allocator>
			to_string(const Allocator& a = Allocator()) const
		{
			return std::basic_string<CharT, Traits, Allocator>(_data, _size, a);
		}

		template<class Allocator>
		explicit operator std::basic_string<CharT, Traits, Allocator>() const
		{
			return std::basic_string<CharT, Traits, Allocator>(_data, _size);
		}

		size_type copy(CharT* dest, size_type count, size_type pos = 0) const
		{
			if(pos >= _size)
			{
				throw std::out_of_range("out of range");
			}
			auto rcount = std::min(count, _size - pos);
			std::copy_n(_data + pos, rcount, dest);
			return rcount;
		}

		constexpr basic_string_view substr(size_type pos = 0, size_type count = npos) const
		{
			if(pos > _size)
			{
				throw std::out_of_range("out of range");
			}
			auto rcount = std::min(count, _size - pos);

			return basic_string_view(_data + pos, rcount);
		}

		constexpr int compare(basic_string_view v) const noexcept
		{
			auto rlen = std::min(size(), v.size());

			auto r = traits_type::compare(data(), v.data(), rlen);

			if(r == 0)
			{
				if(size() == v.size())
				{
					return 0;
				}
				return size() < v.size() ? -1 : 1;
			}

			return r;
		}

		constexpr int compare(size_type pos1, size_type count1, basic_string_view v) const
		{
			return substr(pos1, count1).compare(v);
		}
		constexpr int compare(size_type pos1, size_type count1, basic_string_view v, size_type pos2, size_type count2) const
		{
			return substr(pos1, count1).compare(v.substr(pos2, count2));
		}
		constexpr int compare(const CharT* s) const
		{
			return compare(basic_string_view(s));
		}
		constexpr int compare(size_type pos1, size_type count1, const CharT* s) const
		{
			return substr(pos1, count1).compare(basic_string_view(s));
		}
		constexpr int compare(size_type pos1, size_type count1, const CharT* s, size_type count2) const
		{
			return substr(pos1, count1).compare(basic_string_view(s, count2));
		}

		constexpr size_type find(basic_string_view v, size_type pos = 0) const noexcept
		{
			if(v.size() == 0)
			{
				return pos <= _size ? pos : npos;
			}

			if(pos >= _size)
			{
				return npos;
			}
			auto t = std::search(begin() + pos, end(), v.begin(), v.end(), traits_type::eq);
			if(t != end())
			{
				return std::distance(begin(), t);
			}
			return npos;
		}

		constexpr size_type find(CharT c, size_type pos = 0) const noexcept
		{
			if(pos >= _size)
			{
				return npos;
			}
			while(pos < _size)
			{
				if(traits_type::eq(_data[pos], c))
				{
					return pos;
				}
				pos++;
			}
			return npos;
		}

		constexpr size_type find(const CharT* s, size_type pos, size_type count) const
		{
			return find(basic_string_view(s, count), pos);
		}

		constexpr size_type find(const CharT* s, size_type pos = 0) const
		{
			return find(basic_string_view(s), pos);
		}

		//////////////////////////////////////////////////////////////////////////
		constexpr size_type rfind(basic_string_view v, size_type pos = npos) const noexcept
		{
			return rfind(v.data(), pos, v.size());
		}

		constexpr size_type rfind(CharT c, size_type pos = npos) const noexcept
		{
			return rfind(&c, pos, 1);
		}

		constexpr size_type rfind(const CharT* s, size_type pos, size_type count) const
		{
			if(count <= this->_size)
			{
				pos = std::min(size_type(this->_size - count), pos);
				do
				{
					if(traits_type::compare(this->_data + pos, s, count) == 0)
						return pos;
				} while(pos-- > 0);
			}
			return npos;

		}
		constexpr size_type rfind(const CharT* s, size_type pos = npos) const
		{
			return rfind(basic_string_view(s), pos);
		}

		//////////////////////////////////////////////////////////////////////////
		constexpr size_type find_first_of(basic_string_view v, size_type pos = 0) const noexcept
		{
			auto t = std::find_first_of(begin() + pos, end(), v.begin(), v.end(), traits_type::eq);
			if(t != end())
			{
				return std::distance(begin(), t);
			}
			return npos;
		}
		constexpr size_type find_first_of(CharT c, size_type pos = 0) const noexcept
		{
			return find(c, pos);
		}
		constexpr size_type find_first_of(const CharT* s, size_type pos, size_type count) const
		{
			return find_first_of(basic_string_view(s, count), pos);
		}
		constexpr size_type find_first_of(const CharT* s, size_type pos = 0) const
		{
			return find_first_of(basic_string_view(s), pos);
		}

		//////////////////////////////////////////////////////////////////////////

		constexpr size_type find_last_of(basic_string_view v, size_type pos = npos) const noexcept
		{
			return find_last_of(v.data(), pos, v.size());
		}
		constexpr size_type find_last_of(CharT c, size_type pos = npos) const noexcept
		{
			return rfind(c, pos);
		}
		constexpr size_type find_last_of(const CharT* s, size_type pos, size_type count) const
		{
			size_type __size = this->size();
			if(__size && count)
			{
				if(--__size > pos)
					__size = pos;
				do
				{
					if(traits_type::find(s, count, this->data()[__size]))
						return __size;
				} while(__size-- != 0);
			}
			return npos;
		}
		constexpr size_type find_last_of(const CharT* s, size_type pos = npos) const
		{
			return find_last_of(basic_string_view(s), pos);
		}

		//////////////////////////////////////////////////////////////////////////
		constexpr size_type find_first_not_of(basic_string_view v, size_type pos = 0) const noexcept
		{
			if(pos < size())
			{
				auto first = begin() + pos;

				for(; first != end(); ++first)
				{
					bool found = false;
					for(auto it = v.begin(); it != v.end(); ++it)
					{
						if(traits_type::eq(*first, *it))
						{
							found = true;
							break;
						}
					}
					if(!found)
					{
						return std::distance(begin(), first);
					}
				}
			}
			return npos;
		}

		constexpr size_type find_first_not_of(CharT c, size_type pos = 0) const noexcept
		{
			if(pos < size())
			{
				auto first = begin() + pos;

				for(; first != end(); ++first)
				{
					if(!traits_type::eq(*first, c))
					{
						return std::distance(begin(), first);
					}
				}
			}
			return npos;
		}

		constexpr size_type find_first_not_of(const CharT* s, size_type pos, size_type count) const
		{
			return find_first_not_of(basic_string_view(s, count), pos);
		}

		constexpr size_type find_first_not_of(const CharT* s, size_type pos = 0) const
		{
			return find_first_not_of(basic_string_view(s), pos);
		}

		//////////////////////////////////////////////////////////////////////////
		constexpr size_type find_last_not_of(basic_string_view v, size_type pos = npos) const noexcept
		{
			return find_last_not_of(v.data(), pos, v.size());
		}

		constexpr size_type find_last_not_of(CharT c, size_type pos = npos) const noexcept
		{
			return find_last_not_of(&c, pos, 1);
		}

		constexpr size_type find_last_not_of(const CharT* s, size_type pos, size_type count) const
		{
			size_type __size = this->_size;
			if(__size)
			{
				if(--__size > pos)
					__size = pos;
				do
				{
					if(!traits_type::find(s, count, this->data()[__size]))
						return __size;
				} while(__size--);
			}

			return npos;
		}

		constexpr size_type find_last_not_of(const CharT* s, size_type pos = npos) const
		{
			return find_last_not_of(basic_string_view(s), pos);
		}

	private:
		const_pointer	_data;
		size_type		_size;
	};

	template<typename _CharT, typename _Traits>
	inline bool	operator==(basic_string_view<_CharT, _Traits> __x,
						   basic_string_view<_CharT, _Traits> __y)
	{
		return __x.compare(__y) == 0;
	}


#if 0
	//HACK:  VS2015 bug 
	template<typename _CharT, typename _Traits>
	inline bool	operator==(basic_string_view<_CharT, _Traits> __x,
						   std::remove_reference_t <basic_string_view<_CharT, _Traits> > __y)
	{
		return __x.compare(__y) == 0;
	}

	template<typename _CharT, typename _Traits>
	inline bool operator==(std::remove_reference_t<basic_string_view<_CharT, _Traits>> __x,
						   basic_string_view<_CharT, _Traits> __y)
	{
		return __x.compare(__y) == 0;
	}
#else

	template<typename _CharT, typename _Traits,
		class U, class = std::enable_if_t< std::is_convertible<U, basic_string_view<_CharT, _Traits>>::value >
	>
		inline bool	operator==(basic_string_view<_CharT, _Traits> __x, U&& __y)
	{
		return __x == basic_string_view<_CharT, _Traits>(__y);
	}

	template<typename _CharT, typename _Traits,
		class U, class = std::enable_if_t< std::is_convertible<U, basic_string_view<_CharT, _Traits>>::value >
	>
		inline bool	operator==(U&& __y, basic_string_view<_CharT, _Traits> __x)
	{
		return basic_string_view<_CharT, _Traits>(__y) == __x;
	}

#endif

	template<typename _CharT, typename _Traits>
	inline bool
		operator!=(basic_string_view<_CharT, _Traits> __x,
				   basic_string_view<_CharT, _Traits> __y)
	{
		return __x.compare(__y) != 0;
	}


#if 0
	template<typename _CharT, typename _Traits>
	inline bool
		operator!=(basic_string_view<_CharT, _Traits> __x,
				   std::remove_reference_t<basic_string_view<_CharT, _Traits>> __y)
	{
		return __x.compare(__y) != 0;
	}

	template<typename _CharT, typename _Traits>
	inline bool
		operator!=(std::remove_reference_t<basic_string_view<_CharT, _Traits>> __x,
				   basic_string_view<_CharT, _Traits> __y)
	{
		return __x.compare(__y) != 0;
	}

#else

	template<typename _CharT, typename _Traits,
		class U, class = std::enable_if_t< std::is_convertible<U, basic_string_view<_CharT, _Traits>>::value >
	>
		inline bool	operator!=(basic_string_view<_CharT, _Traits> __x, U&& __y)
	{
		return __x != basic_string_view<_CharT, _Traits>(__y);
	}

	template<typename _CharT, typename _Traits,
		class U, class = std::enable_if_t< std::is_convertible<U, basic_string_view<_CharT, _Traits>>::value >
	>
		inline bool	operator!=(U&& __y, basic_string_view<_CharT, _Traits> __x)
	{
		return basic_string_view<_CharT, _Traits>(__y) != __x;
	}

#endif




	template<typename _CharT, typename _Traits>
	inline bool
		operator< (basic_string_view<_CharT, _Traits> __x,
				   basic_string_view<_CharT, _Traits> __y)
	{
		return __x.compare(__y) < 0;
	}


#if 0
	template<typename _CharT, typename _Traits>
	inline bool operator< (basic_string_view<_CharT, _Traits> __x,
						   std::remove_reference_t<basic_string_view<_CharT, _Traits>> __y)
	{
		return __x.compare(__y) < 0;
	}

	template<typename _CharT, typename _Traits>
	inline bool	operator< (std::remove_reference_t<basic_string_view<_CharT, _Traits>> __x,
						   basic_string_view<_CharT, _Traits> __y)
	{
		return __x.compare(__y) < 0;
	}

#else

	template<typename _CharT, typename _Traits,
		class U, class = std::enable_if_t< std::is_convertible<U, basic_string_view<_CharT, _Traits>>::value >
	>
		inline bool	operator<(basic_string_view<_CharT, _Traits> __x, U&& __y)
	{
		return __x < basic_string_view<_CharT, _Traits>(__y);
	}

	template<typename _CharT, typename _Traits,
		class U, class = std::enable_if_t< std::is_convertible<U, basic_string_view<_CharT, _Traits>>::value >
	>
		inline bool	operator<(U&& __x, basic_string_view<_CharT, _Traits> __y)
	{
		return basic_string_view<_CharT, _Traits>(__x) < __y;
	}

#endif


	template<typename _CharT, typename _Traits>
	inline bool
		operator<=(basic_string_view<_CharT, _Traits> __x,
				   basic_string_view<_CharT, _Traits> __y) noexcept
	{
		return __x.compare(__y) <= 0;
	}


#if 0

	template<typename _CharT, typename _Traits>
	inline bool
		operator<=(basic_string_view<_CharT, _Traits> __x,
				   std::remove_reference_t<basic_string_view<_CharT, _Traits>> __y) noexcept
	{
		return __x.compare(__y) <= 0;
	}

	template<typename _CharT, typename _Traits>
	inline bool
		operator<=(std::remove_reference_t<basic_string_view<_CharT, _Traits>> __x,
				   basic_string_view<_CharT, _Traits> __y) noexcept
	{
		return __x.compare(__y) <= 0;
	}

#else

	template<typename _CharT, typename _Traits,
		class U, class = std::enable_if_t< std::is_convertible<U, basic_string_view<_CharT, _Traits>>::value >
	>
		inline bool	operator<=(basic_string_view<_CharT, _Traits> __x, U&& __y)
	{
		return __x <= basic_string_view<_CharT, _Traits>(__y);
	}

	template<typename _CharT, typename _Traits,
		class U, class = std::enable_if_t< std::is_convertible<U, basic_string_view<_CharT, _Traits>>::value >
	>
		inline bool	operator<=(U&& __x, basic_string_view<_CharT, _Traits> __y)
	{
		return basic_string_view<_CharT, _Traits>(__x) <= __y;
	}

#endif



	template<typename _CharT, typename _Traits>
	inline bool operator > (basic_string_view<_CharT, _Traits> __x,
							basic_string_view<_CharT, _Traits> __y)
	{
		return __x.compare(__y) > 0;
	}

#if 0

	template<typename _CharT, typename _Traits>
	inline bool	operator > (basic_string_view<_CharT, _Traits> __x,
							std::remove_reference_t<basic_string_view<_CharT, _Traits>> __y)
	{
		return __x.compare(__y) > 0;
	}

	template<typename _CharT, typename _Traits>
	inline bool	operator > (std::remove_reference_t<basic_string_view<_CharT, _Traits>> __x,
							basic_string_view<_CharT, _Traits> __y)
	{
		return __x.compare(__y) > 0;
	}

#else

	template<typename _CharT, typename _Traits,
		class U, class = std::enable_if_t< std::is_convertible<U, basic_string_view<_CharT, _Traits>>::value >
	>
		inline bool	operator>(basic_string_view<_CharT, _Traits> __x, U&& __y)
	{
		return __x > basic_string_view<_CharT, _Traits>(__y);
	}

	template<typename _CharT, typename _Traits,
		class U, class = std::enable_if_t< std::is_convertible<U, basic_string_view<_CharT, _Traits>>::value >
	>
		inline bool	operator>(U&& __y, basic_string_view<_CharT, _Traits> __x)
	{
		return basic_string_view<_CharT, _Traits>(__y) > __x;
	}

#endif





	template<typename _CharT, typename _Traits>
	inline bool	operator>=(basic_string_view<_CharT, _Traits> __x,
						   basic_string_view<_CharT, _Traits> __y) noexcept
	{
		return __x.compare(__y) >= 0;
	}


#if 0

	template<typename _CharT, typename _Traits>
	inline bool	operator>=(basic_string_view<_CharT, _Traits> __x,
						   std::remove_reference_t<basic_string_view<_CharT, _Traits>> __y) noexcept
	{
		return __x.compare(__y) >= 0;
	}

	template<typename _CharT, typename _Traits>
	inline bool	operator>=(std::remove_reference_t<basic_string_view<_CharT, _Traits>> __x,
						   basic_string_view<_CharT, _Traits> __y) noexcept
	{
		return __x.compare(__y) >= 0;
	}

#else

	template<typename _CharT, typename _Traits,
		class U, class = std::enable_if_t< std::is_convertible<U, basic_string_view<_CharT, _Traits>>::value >
	>
		inline bool	operator>=(basic_string_view<_CharT, _Traits> __x, U&& __y)
	{
		return __x >= basic_string_view<_CharT, _Traits>(__y);
	}

	template<typename _CharT, typename _Traits,
		class U, class = std::enable_if_t< std::is_convertible<U, basic_string_view<_CharT, _Traits>>::value >
	>
		inline bool	operator>=(U&& __y, basic_string_view<_CharT, _Traits> __x)
	{
		return basic_string_view<_CharT, _Traits>(__y) >= __x;
	}

#endif


	template <class CharT, class Traits>
	std::basic_ostream<CharT, Traits>&	operator<<(std::basic_ostream<CharT, Traits>& os, basic_string_view <CharT, Traits> v)
	{
		os << v.to_string();
		return os;
	}

	using string_view = basic_string_view < char >;
	using wstring_view = basic_string_view < wchar_t >;
	using u16string_view = basic_string_view < char16_t >;
	using u32string_view = basic_string_view < char32_t >;

}


namespace std {


	template<class CharT, class Traits>
	struct hash <cpp::basic_string_view< CharT, Traits> >
	{
		size_t operator() (cpp::basic_string_view< CharT, Traits> const& sv) const
		{
			return std::hash<typename cpp::basic_string_view< CharT, Traits>::pointer>{}(sv.data()) ^ std::hash<std::size_t>{}(sv.size());
		}
	};
}


namespace cpp {

#if !defined(CPPNEXT_NO_USER_DEFINED_LITERAL)

	inline namespace literals
	{
		inline namespace string_view_literals
		{

			inline  basic_string_view<char> operator"" _sv(const char* __str, size_t __len)
			{
				return basic_string_view<char>{__str, __len};
			}

			inline  basic_string_view<wchar_t> operator"" _sv(const wchar_t* __str, size_t __len)
			{
				return basic_string_view<wchar_t>{__str, __len};
			}

			inline  basic_string_view<char16_t> operator"" _sv(const char16_t* __str, size_t __len)
			{
				return basic_string_view<char16_t>{__str, __len};
			}

			inline  basic_string_view<char32_t>	operator"" _sv(const char32_t* __str, size_t __len)
			{
				return basic_string_view<char32_t>{__str, __len};
			}
		}
	}

#endif

}


#include "config/config_end.h"